//
// Created by apple on 2022/9/24.
//
#include "matrix.h"
#include <iostream>
#include <chrono>

#define TEST_NUM 5

void valid(int a, int b) {
    printf("---------------------------------------------\n");
    printf("Check the accuracy of each algorithm:\n");
    printf("---------------------------------------------\n");

    auto *m1 = new Matrix(a, b);
    auto *m2 = new Matrix(b, a);
    m1->setRandom();
    m2->setRandom();
    std::cout << "Matrix 1:" << std::endl;
    m1->print();
    std::cout << "Matrix 2:" << std::endl;
    m2->print();
    printf("---------------------------------------------\n");

    std::cout << "Baseline Result:" << std::endl;
    matmulBaseline(m1, m2)->print();

    std::cout << "Winergrad Transpose Result:" << std::endl;
    matmulWinergrad(m1, m2)->print();


    std::cout << "Baseline Multi Thread Result:" << std::endl;
    matmulBaselineMultiThread(m1, m2)->print();

    std::cout << "ikj Order Result:" << std::endl;
    matmulIKJ(m1, m2)->print();

    std::cout << "ikj Winergrad Result:" << std::endl;
    matmulIKJWinergrad(m1, m2)->print();

    std::cout << "ikj Multi Thread Result:" << std::endl;
    matmulIKJMultiThread(m1, m2)->print();

    std::cout << "Block Result:" << std::endl;
    matmulBlock(m1, m2)->print();
    std::cout << "Block Winergrad Result:" << std::endl;
    matmulBlockWinergrad(m1, m2)->print();

    std::cout << "Block Multi Thread Result:" << std::endl;
    matmulBlockMultiTread(m1, m2)->print();
}


void timeMatmul(Matrix *(func)(Matrix *, Matrix *), const std::string &func_name, Matrix *A, Matrix *B) {
    auto t1 = std::chrono::system_clock::now();
    for (int i = 0; i < TEST_NUM; ++i)
        func(A, B);
    auto t2 = std::chrono::system_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1);
    std::cout << func_name << " cost: " << duration.count() / TEST_NUM << "s" << std::endl;
}

void timeThem(int a, int b) {
    printf("---------------------------------------------\n");
    printf("Check the time of each algorithm:\n");
    printf("* Loop %d times each, and show the mean time\n", TEST_NUM);
    printf("* Using thread num %d for multi thread version\n", THREAD_NUM);
    printf("* Shapes are (%d, %d), and (%d, %d)\n", a, b, b, a);
    printf("---------------------------------------------\n");
    auto *m1 = new Matrix(a, b);
    auto *m2 = new Matrix(b, a);
    m1->setRandom();
    m2->setRandom();
    if (a <= 3000 && b <= 3000) {
        timeMatmul(matmulBaseline, "Baseline", m1, m2);
        timeMatmul(matmulWinergrad, "Winergrad Transpose", m1, m2);
        timeMatmul(matmulBaselineMultiThread, "Baseline Multi Thread", m1, m2);
        timeMatmul(matmulIKJ, "IKJ order", m1, m2);
        timeMatmul(matmulIKJWinergrad, "IKJ Winergrad", m1, m2);
    }

    timeMatmul(matmulIKJMultiThread, "IKJ Multi Thread", m1, m2);
    timeMatmul(matmulBlock, "Block", m1, m2);
    timeMatmul(matmulBlockWinergrad, "Block Winergrad", m1, m2);
    timeMatmul(matmulBlockMultiTread, "Block Multi Thread", m1, m2);
}


int main() {
    valid(2, 3);
    timeThem(100, 100);
    timeThem(1000, 1000);
    timeThem(2000, 2000);
//    timeThem(10000, 10000);
}
